<template>
  <q-card class="bg-transparent">
    <div
      class="text-h4 text-grey-6 absolute-center"
      style="letter-spacing: 5px; top: 15%"
    >
      {{ $t(lang + "远程遥控") }}
    </div>
    <div
      class="text-h5 text-grey-6 absolute-center"
      style="letter-spacing: 5px; margin-top: -125px"
    >
      {{ $t(lang + "向前移动") }}
    </div>
    <div
      class="text-h5 text-grey-6 absolute-center"
      style="letter-spacing: 5px; margin-top: 125px"
    >
      {{ $t(lang + "向后移动") }}
    </div>
    <div
      class="text-h5 text-grey-6 absolute-center"
      style="
        writing-mode: vertical-lr;
        letter-spacing: 5px;
        margin-left: -125px;
      "
    >
      {{ $t(lang + "向左旋转") }}
    </div>
    <div
      class="text-h5 text-grey-6 absolute-center"
      style="writing-mode: vertical-lr; letter-spacing: 5px; margin-left: 125px"
    >
      {{ $t(lang + "向右旋转") }}
    </div>
    <div class="absolute-center row" style="margin-top: 250px">
      <div
        class="col-2 text-h5 text-grey-6"
        style="writing-mode: vertical-lr; letter-spacing: 5px"
      >
        {{ $t(lang + "遥控速度") }}
      </div>
      <div class="col-10">
        <slider-item
          :label="$t(lang + '线性速度')"
          input-label="linear"
          color="secondary"
          v-model="linearRatio"
          :min="0.1"
          :max="1"
          :step="0.2"
        />
        <slider-item
          :label="$t(lang + '旋转速度')"
          input-label="angular"
          color="secondary"
          v-model="angularRatio"
          :min="0.1"
          :max="1"
          :step="0.1"
        />
      </div>
    </div>
    <div>
      <q-btn
        no-caps
        class="absolute text-space"
        :label="$t(lang + '创建点位')"
        :class="ui.modelClass"
        @click="pointEdit.show()"
        style="top: 150px; right: 0; border-radius: 25px 0 0 25px"
      >
      </q-btn>
    </div>
    <div ref="joystick" />
    <point-edit-big ref="pointEdit" @createPoint="createPoint" />
  </q-card>
</template>

<script setup>
import nipplejs from "nipplejs";
import { onBeforeMount, onMounted, onUnmounted, ref } from "vue";
import { Notify, useQuasar } from "quasar";
import SliderItem from "components/common/SliderItem.vue";
import { useI18n } from "vue-i18n";
import { useUiStore } from "stores/ui";
import PointEditBig from "components/map/PointEditBig.vue";
import { useAPIStore } from "stores/api";
import { api } from "boot/axios";

const mapId = ref();
const t = useI18n().t;
const lang = "remoteControl.remoteControl."; // 当前路由名 加上 当前文件名首字母 用于双语
const ui = useUiStore();
const $q = useQuasar();

let ws = null;
const url = "ws://" + document.domain + ":9090";
const connected = ref(false);
const alive = true;
let rec;

const pointEdit = ref();
const robotPose = ref({}); // 设备实时位置

// 创建连接
function createWs() {
  ws = new WebSocket(url);
  initWs();
}

// 初始化
function initWs() {
  ws.onclose = () => {
    connected.value = false;
  };

  ws.onerror = () => {
    connected.value = false;
    reConnect();
  };

  ws.onopen = () => {
    connected.value = true;
    heartCheck.start();
    Notify.create({ type: "positive", message: t(lang + "已建立与ROS的直连") });
    subscribe("/robot_pose");
  };

  ws.onmessage = (e) => {
    wsOnMessage(e);
  };
}

// 重连
function reConnect() {
  console.log("尝试重连");
  if (connected.value || !alive) return;
  rec && clearTimeout(rec);
  rec = setTimeout(createWs, 5000);
}

// 设置心跳
const heartCheck = {
  timeoutObj: null,
  start: () => {
    heartCheck.timeoutObj = setTimeout(function () {
      if (!connected.value && alive) reConnect();
    }, 10000);
  },
  reset: () => {
    clearTimeout(heartCheck.timeoutObj);
    heartCheck.start();
  },
};

// 发送消息
function wsSend(object) {
  if (ws.readyState === 1) {
    ws.send(JSON.stringify(object));
  } else {
    if (connected.value) {
      setTimeout(() => wsSend(object), 200);
    }
  }
}

function publish(topic, msg) {
  wsSend({ op: "publish", topic, msg });
}

function subscribe(topic) {
  wsSend({ op: "subscribe", topic });
}

// 订阅topic消息
function wsOnMessage(e) {
  heartCheck.reset();
  const resData = JSON.parse(e.data);
  switch (resData.op.valueOf()) {
    case "publish":
      if (resData.topic === "/robot_pose") {
        robotPose.value = resData.msg;
      }
      break;
  }
}

let timer;

onBeforeMount(() => {
  mapId.value = document.location.hash.slice(
    document.location.hash.indexOf("=") + 1
  );
});

onMounted(() => {
  // 获取机器人当前位置
  createWs();
  initJoyStick();
  timer = setInterval(() => {
    pubVel(linear.value, angular.value);
  }, 50);
});

onUnmounted(() => {
  clearInterval(timer);
});

const joystick = ref();
const manager = ref(null);
const linear = ref(0);
const angular = ref(0);
const linearRatio = ref(0.4);
const angularRatio = ref(0.4);

const twist = ref({
  linear: { x: 0, y: 0, z: 0 },
  angular: { x: 0, y: 0, z: 0 },
});

function pubVel(x, z) {
  twist.value.linear.x = x;
  twist.value.angular.z = z;
  publish("/device_send_raw", "SET_STATE,NORMAL｜～");
  publish("/web/cmd_vel", twist.value);
}

// 初始化
function initJoyStick() {
  const options = {
    zone: joystick.value,
    mode: "static", // mode: 'semi','dynamic'
    position: { left: "50%", bottom: "50%" },
    color: "blue",
    size: 200,
  };
  manager.value = nipplejs.create(options);
  manager.value
    .on("start end", function () {
      linear.value = 0;
      angular.value = 0;
    })
    .on("move", function (evt, data) {
      if (
        Math.trunc((data.angle.degree + 45) / 90) === 1 ||
        Math.trunc((data.angle.degree + 45) / 90) === 3
      ) {
        angular.value = 0;
        linear.value = data.vector.y * linearRatio.value;
      } else {
        angular.value = -(data.vector.x * angularRatio.value);
        linear.value = 0;
      }
    });
}

// 新建点位
function createPoint(data) {
  if (!data.value.name) {
    $q.notify({
      message: t(lang + "点位名称未填写"),
      color: "negative",
      position: "top-right",
    });
  } else {
    $q.dialog({
      dark: ui.isDark,
      title: t(lang + "创建点位"),
      message: t(lang + "将在机器人处创建点位") + data.value.name,
      ok: t("ok"),
      cancel: t("cancel"),
    }).onOk(() => {
      const params = {
        name: data.value.name,
        type: data.value.type ? data.value.type.value : data.value.type,
        mapID: mapId.value,
        pose: robotPose.value,
      };
      api.post(useAPIStore().mapUrl + "mapPose/ros", params).then(() => {
        pointEdit.value.close();
        $q.notify({
          message: t(lang + "点位") + data.value.name + t(lang + "创建成功"),
          color: "positive",
          position: "top-right",
        });
      });
    });
  }
}
</script>
